<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button id="btn">测试按钮</button>

    <h1>补GIAO</h1>
    <!-- 下载进度条 -->
    <progress id="progressBar" value="0" max="100"></progress>

    <video id="video" src="http://vjs.zencdn.net/v/oceans.mp4">下载视频</video>

    <script>
        // 监听对象属性的读取和写入
        let onWatch = (obj, setBind, getLogger) => {
            let handler = {
                get(target, property, receiver) {
                    // 读取属性监听
                    getLogger(target, property)
                    return Reflect.get(target, property, receiver)
                },
                set(target, property, value, receiver) {
                    // 写入属性监听
                    setBind(value, property)
                    return Reflect.set(target, property, value)
                }
            }
            return new Proxy(obj, handler)
        }


        let obj = { a: 1 }

        let p = onWatch(obj, (v, property) => {
            console.log(`属性 ${property} 被设置为 ${v}`)
        }, (v, property) => {
            console.log(`属性 ${property} 被读取，值为 ${v.a}`)
        })

        setTimeout(() => {
            p.a = 2
            console.log(p.a)
        }, 1000)


        // 封装一个ajax请求函数
        function axios(method, url, data = {}, headers = {}) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest()
                xhr.open(method, url, true)
                xhr.onreadystatechange = () => {
                    if (xhr.readyState !== 4) return
                    if (xhr.status === 200) {
                        resolve(xhr.response)
                    } else {
                        reject(new Error(xhr.statusText))
                    }
                }
                xhr.onerror = () => {
                    reject(new Error(xhr.statusText))
                }

                xhr.responseType = 'json'
                xhr.setRequestHeader('Content-Type', 'application/json')
                for (let key in headers) {
                    xhr.setRequestHeader(key, headers[key])
                }
                if (data && Object.keys(data).length > 0) {
                    xhr.send(JSON.stringify(data))
                } else {
                    xhr.send(null)
                }
            })
        }

        // axios('GET', 'https://jsonplaceholder.typicode.com/todos/1').then(res => {
        //     console.log(res)
        // })

        // 发布订阅模式

        class EventBus {
            constructor() {
                this.callback = {}
            }
            on(event, fn) {
                this.callback[event] = this.callback[event] || []
                this.callback[event].push(fn)
            }
            emit(event, ...args) {
                if (this.callback[event]) {
                    this.callback[event].forEach(fn => fn(...args))
                }
            }
            off(event, fn) {
                if (this.callback[event]) {
                    this.callback[event] = this.callback[event].filter(item => item !== fn)
                }
            }
            once(event, fn) {
                const _this = this
                function handler(...args) {
                    fn(...args)
                    _this.off(event, handler)
                }
                this.on(event, handler)
            }
        }

        const eventBus = new EventBus()

        eventBus.on('aaa', (...args) => {
            console.log('aaa', ...args)
        })

        // 防抖
        const debounce = (fn, delay, immediate = false) => {
            let timer = null;
            return (...args) => {
                if (timer) clearTimeout(timer);
                let callNow = immediate && !timer;
                if (callNow) {
                    fn(...args);
                }
                timer = setTimeout(() => {
                    fn(...args);
                }, delay);
            }
        }
        // 节流
        const throttle = (fn, delay, immediate = false) => {
            let timer = null;
            return (...args) => {
                if (timer) return;
                const callNow = immediate && !timer;
                if (callNow) {
                    fn(...args);
                }
                timer = setTimeout(() => {
                    fn(...args);
                    clearTimeout(timer);
                    timer = null;
                }, delay);
            }
        }

        // 深拷贝 使用weakMap解决循环引用问题
        function deepClone(obj, map = new WeakMap()) {
            if (obj === null || typeof obj !== 'object') return obj
            // 正则
            if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
            // 日期
            if (obj instanceof Date) return new Date(obj.getTime())

            if (map.has(obj)) return map.get(obj)
            let cloneObj = Array.isArray(obj) ? [] : {}
            map.set(obj, cloneObj)
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    cloneObj[key] = deepClone(obj[key], map)
                }
            }
            return cloneObj
        }

        // instanceof 实现
        function myInstanceOf(left, right) {
            let rightPrototype = right.prototype;
            left = left.__proto__;
            while (true) {
                if (left === null) return false;
                if (left === rightPrototype) return true;
                left = left.__proto__;
            }
            return false;
        }
        // 测试
        const aa = myInstanceOf({}, Array)
        console.log(aa)
        setTimeout(() => {
            console.log('quit timer');
        }, 0)

        new Promise((resolve, reject) => {
            console.log('init promise');
            Promise.resolve().then(() => { })
        })

        Promise.resolve().then(res => {
            console.log('nextTick');
        })

        setTimeout(() => {
            console.log('timer');
        })

        // 简易promise
        class MyPromise {
            constructor(executor) {
                // 通过 state 字段管理三种状态（pending/fulfilled/rejected）
                this.state = 'pending'
                this.value = undefined
                this.reason = undefined
                this.onFulfilledCallbacks = []
                this.onRejectedCallbacks = []

                const resolve = (value) => {
                    if (this.state === 'pending') {
                        this.state = 'fulfilled'
                        this.value = value
                        this.onFulfilledCallbacks.forEach(fn => fn())
                    }
                }
                const reject = (reason) => {
                    if (this.state === 'pending') {
                        this.state = 'rejected'
                        this.reason = reason
                        this.onRejectedCallbacks.forEach(fn => fn())
                    }
                }

                try {
                    // 错误处理：自动捕获executor和回调中的错误
                    executor(resolve, reject)
                } catch (error) {
                    reject(error)
                }
            }

            then(onFulfilled, onRejected) {
                onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
                onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

                const promise2 = new MyPromise((resolve, reject) => {
                    if (this.state === 'fulfilled') {
                        // 异步队列：使用 setTimeout 实现微任务（Promise/A+规范要求的异步调用）
                        setTimeout(() => {
                            try {
                                const x = onFulfilled(this.value);
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    } else if (this.state === 'rejected') {
                        setTimeout(() => {
                            try {
                                const x = onRejected(this.reason);
                                // 链式调用：通过返回新Promise实现链式调用，支持值穿透
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    } else {
                        this.onFulfilledCallbacks.push(() => {
                            setTimeout(() => {
                                try {
                                    const x = onFulfilled(this.value);
                                    resolvePromise(promise2, x, resolve, reject);
                                } catch (err) {
                                    reject(err);
                                }
                            }, 0);
                        })
                        this.onRejectedCallbacks.push(() => {
                            setTimeout(() => {
                                try {
                                    const x = onRejected(this.reason);
                                    resolvePromise(promise2, x, resolve, reject);
                                } catch (err) {
                                    reject(err);
                                }
                            }, 0);
                        })
                    }
                })
                return promise2
            }
            catch(onRejected) {
                return this.then(null, onRejected)
            }

            static resolve(value) {
                return new MyPromise((resolve) => resolve(value))
            }
            static reject(reason) {
                return new MyPromise((_, reject) => reject(reason))
            }
        }

        function resolvePromise(promise2, x, resolve, reject) {
            if (promise2 === x) {
                return reject(new TypeError('Chaining cycle detected for promise'));
            }
            if (x instanceof MyPromise) {
                x.then(resolve, reject);
            }
            else {
                resolve(x);
            }
        }


        // 数组操作
        // 数组去重 多种方法
        function unique(arr) {
            return Array.from(new Set(arr))
        }
        function unique2(arr) {
            return [...new Set(arr)]
        }
        function unique3(arr) {
            return arr.filter((item, index) => arr.indexOf(item) === index)
        }
        function unique4(arr) {
            const map = new Map()
            return arr.filter(item => !map.has(item) && map.set(item, 1))
        }

        // 数组扁平化
        function flatten(arr) {
            return arr.reduce((pre, cur) => {
                return pre.concat(Array.isArray(cur) ? flatten(cur) : cur)
            }, [])
        }
        function flatten2(arr) {
            while (arr.some(item => Array.isArray(item))) {
                arr = [].concat(...arr)
            }
            return arr
        }

        // 数组去重并排序
        function uniqueAndSort(arr) {
            return Array.from(new Set(arr)).sort((a, b) => a - b)
        }

        // 数组求和
        function sum(arr) {
            return arr.reduce((pre, cur) => pre + cur, 0)
        }

        // 算法题：
        // 递归n叉树最大深度算法
        class Node {
            constructor(val, children = []) {
                this.val = val
                this.children = children
            }
        }
        function maxDepth(root) {
            if (!root) return 0
            if (root.children.length === 0) return 1
            let max = 0
            for (let child of root.children) {
                max = Math.max(max, maxDepth(child))
            }
            return max + 1
        }
        // 构建示例树：
        //         A
        //       / | \
        //      B  C  D
        //     / \
        //    E   F
        const root = new Node('A', [
            new Node('B', [new Node('E'), new Node('F')]),
            new Node('C'),
            new Node('D')
        ]);


        // 冒泡排序
        function bubbleSort(arr) {
            for (let i = 0; i < arr.length; i++) {
                for (let j = 0; j < arr.length - i - 1; j++) {
                    if (arr[j] > arr[j + 1]) {
                        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
                    }
                }
            }
            return arr
        }

        // 快速排序
        function quickSort(arr) {
            if (arr.length <= 1) return arr
            const left = []
            const right = []
            const mid = arr[0]
            for (let i = 1; i < arr.length; i++) {
                if (arr[i] < mid) {
                    left.push(arr[i])
                } else {
                    right.push(arr[i])
                }
            }
            return [...quickSort(left), mid, ...quickSort(right)]
        }

        // 插入排序
        function insertionSort(arr) {
            for (let i = 1; i < arr.length; i++) {
                let j = i
                while (j > 0 && arr[j] < arr[j - 1]) {
                    [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]
                    j--
                }
            }
            return arr
        }

        // 字符串反转
        function reverseString(str) {
            return str.split('').reverse().join('')
        }

        // 将字符串中的每个单词的首字母大写，其余字母小写。
        function capitalizeWords(str) {
            return str.split(' ').map(word => {
                return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
            }).join(' ')
        }

        // 判断一个字符串是否为回文(noon这种)
        function isPalindrome(str) {
            return str === reverseString(str)
        }
        function isPalindrome2(str) {
            let left = 0
            let right = str.length - 1
            while (left < right) {
                if (str[left] !== str[right]) {
                    return false
                }
                left++
                right--
            }
            return true
        }
        function isPalindrome3(str) {
            return str.split('').every((val, index, arr) => {
                return val === arr[arr.length - index - 1]
            })
        }

        const obj1 = {
            a: '1111'
        }
        const obj2 = {
            a: '2222'
        }

        function aaa(obj) {
            obj.a = '3333'
            obj = obj2;
            return obj.a
        }

        console.log(aaa(obj1))
        console.log(obj2.a)


        // 调用系统下载带进度条
        async function downloadWithProgress(url, fileName = 'video.mp4') {
            const response = await fetch(url);
            const contentLength = response.headers.get('content-length');
            const readableStream = response.body;

            const fileStream = streamSaver.createWriteStream(fileName, {
                size: contentLength // 声明文件大小以激活进度计算
            });

            const handle = await window.showSaveFilePicker({
                suggestedName: 'video.mp4',
                types: [{
                    description: '所有文件',
                    accept: { '*/*': ['.*'] },
                }],
            });

            // 4. 创建可写流并传输数据
            const writable = await handle.createWritable();
            await response.body.pipeTo(writable);
            await writable.close();
            fileStream.getWriter().close();
        }
        // 触发下载的函数
        // async function downloadWithProgress(url) {
        //     try {
        //         const response = await fetch(url);
        //         if (!response.ok) throw new Error('网络响应错误');

        //         const total = +response.headers.get('Content-Length');
        //         let loaded = 0;

        //         const reader = response.body.getReader();
        //         const chunks = [];

        //         while (true) {
        //             const { done, value } = await reader.read();
        //             if (done) break;
        //             chunks.push(value);
        //             loaded += value.length;
        //             updateProgress(loaded, total); // 更新进度条
        //         }

        //         const blob = new Blob(chunks);
        //         const downloadUrl = URL.createObjectURL(blob);
        //         const a = document.createElement('a');
        //         a.href = downloadUrl;
        //         a.download = 'video.mp4';
        //         a.click();
        //         URL.revokeObjectURL(downloadUrl);

        //     } catch (error) {
        //         console.error('下载失败:', error);
        //     }
        // }

        // 更新进度条函数
        function updateProgress(loaded, total) {
            const percent = total ? (loaded / total * 100).toFixed(2) : 100;
            console.log(`下载进度: ${percent}%`);
            // 更新DOM进度条
        }


        async function advancedDownload(url) {
            try {
                // 1. 请求用户选择目录
                const directoryHandle = await window.showDirectoryPicker();

                // 2. 在目录中创建新文件
                const fileName = 'new-file.mp4';
                const fileHandle = await directoryHandle.getFileHandle(fileName, { create: true });
                const fileContent = new Blob([new ArrayBuffer(1024 * 1024)], { type: 'video/mp4' });

                // 3. 创建可写流并写入内容
                const writableStream = await fileHandle.createWritable();
                await writableStream.write(fileContent);
                await writableStream.close();

                console.log('文件保存成功:', fileName);
            } catch (err) {
                // 处理用户取消或错误
                if (err.name === 'AbortError') {
                    console.log('用户取消了操作');
                } else {
                    console.error('保存失败:', err);
                }
            }
        }

        // 测试
        const btn = document.getElementById('btn')
        const aaacc = (aaa) => {
            console.log('123')
            console.log(aaa, 'aaa');
        }
        btn.addEventListener('click', debounce(() => aaacc('cccc'), 1000, true))


        window.onbeforeunload = function () {
            window.opener.postMessage('closed', '*');
        };


    </script>
    <script src="
    https://cdn.jsdelivr.net/npm/streamsaver@2.0.6/StreamSaver.min.js
    "></script>

</body>

</html>